#pragma once

#include <iostream>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <memory>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/*
    HTTP是基于TCP协议的，以下TcpServer demo是为了演示从服务端 看到浏览器的请求是什么样子的。
*/

#define BACKLOG 5
class TcpServer
{
private:
    // 不需要 ip 地址  inet_addr()【云服务使用】
    int _port;
    int _listen_sock;
    static std::mutex _mutex;
private:
    TcpServer(int port)
        : _port(port), _listen_sock(-1)
    {
        //InitServer();
    }
    TcpServer(const TcpServer&) = delete;
    TcpServer& operator=(const TcpServer&) = delete;

public:

    static TcpServer& GetInstance(int port)
    {
        // std::unique_lock<std::mutex> lock(_mutex);
        // if(_inst == nullptr)
        // {
        //     _inst = new TcpServer(port);
        //     _inst->InitServer();
        // }
        // return _inst;
        
        //C++11线程安全方法的懒汉模式
        static TcpServer instance(port);
        return instance;
    }
    void InitServer()
    {
        Socket();
        Bind();
        Listen();
        printf("listening...\n");
    }
    int Sock() {return _listen_sock;}
    void Socket()
    {
        _listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sock < 0)
        {
            exit(1); // 创建套接字失败，错误码被设置
        }
        /*socket地址复用：
                当 TCP 连接关闭时，主动关闭的一方会进入 TIME_WAIT 状态，持续约 2-4 分钟。
                在此期间，该端口无法被重新绑定。启用SO_REUSEADDR后，即使端口处于 TIME_WAIT 状态，
                也可以立即重新绑定，避免 “Address already in use” 错误
        */
        int opt = 1;
        setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    }
    void Bind()
    {
        struct sockaddr_in local;         // <arpa/inet.h>
        memset(&local, 0, sizeof(local)); // cstring
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY; // （随即绑定本主机ip） ！！云服务器不能直接绑定公网 ip

        if (bind(_listen_sock, (const sockaddr *)&local, sizeof(local)) < 0)
        {
            exit(2);
        }
    }
    void Listen()
    {
        if (listen(_listen_sock, BACKLOG) < 0)
        {
            exit(3);
        }
    }
    ~TcpServer()
    {

    }
};